﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OS
{
    public static class UserStorage
    {
        private static String[,] physicalMemory = new String[300, 4];

        public static String[,] PhysicalMemory
        {
            get { return physicalMemory; }
            set { physicalMemory = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="location">Virtual address</param>
        /// <param name="dataLine">Line to store in memory</param>
        /// /// <param name="PTR">Location of PTR</param>
        public static void WriteToMemory(int location, String dataLine, int PTR)
        {
            int page = location / 10;
            int offset = location % 10;

            string temp = UserStorage.PhysicalMemory[PTR + page, 0];

            if(temp.Equals("&"))
            {
                Trace.WriteToTrace("Virtual address: " + location);
                throw new PageFaultException();
            }

            int frame = int.Parse(temp);

            while (dataLine.Length < 4)
            {
                dataLine += " ";
            }

            for (int i = 0; i < 4; i++)
            {
                UserStorage.PhysicalMemory[frame + offset, i] = dataLine[i].ToString();
            }

            Trace.WriteToTrace("Virtual address: " + location + ", Physical address: " + (frame + offset));

        }//end WriteToMemory

        public static String ReadFromMemory(int location, int PTR, ProcessControlBlock p)
        {
            int page = location / 10;
            int offset = location % 10;

            if (QueueManager.readyQueue.Peek().TLB.ContainsKey(page))
            {
                Frame frame = (Frame) QueueManager.readyQueue.Peek().TLB[page];
                String str = "";

                for (int i = offset * 4; i < (offset * 4) + 4; i++)
                {
                    str += frame.Data[i];
                }

                return str;
            }
            else
            {
                throw new PageFaultException();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="location">Virtual Address</param>
        /// <param name="PTR">Location of PTR</param>
        /// <returns></returns>
        public static String ReadFromMemory(int location, int PTR)
        {
            int page = location / 10;
            int offset = location % 10;

            string temp = UserStorage.PhysicalMemory[PTR + page, 0];

            if (temp.Equals("&"))
            {
                Trace.WriteToTrace("Virtual address: " + location);
                throw new PageFaultException();
            }

            int frame = int.Parse(temp);

            String data = "";

            for (int i = 0; i < 4; i++)
            {
                data += UserStorage.PhysicalMemory[frame + offset, i];
            }

            if (data == null)
            {
                throw new PageFaultException();
            }

            Trace.WriteToTrace("Virtual address: " + location + ", Physical address: " + (frame + offset));
            return data;

        }//end ReadFromMemory

        /// <summary>
        /// Resets the userMemory 2D array
        /// </summary>
        public static void ClearMemory()
        {
            physicalMemory = new String[300, 4];
        }

        public static void WriteToMemoryAndAllocateBlock(int location, String dataLine, int PTR)
        {
            int page = location / 10;
            int offset = location % 10;

            string temp = UserStorage.PhysicalMemory[PTR + page, 0];

            if (temp.Equals("&"))
            {
                Trace.WriteToTrace("Virtual address: " + location);
                throw new PageFaultException();
            }

            int frame = int.Parse(temp);

            while (dataLine.Length < 4)
            {
                dataLine += " ";
            }

            if (UserStorage.PhysicalMemory[frame, 0] == null)
            {
                for (int i = 0; i < 10; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        UserStorage.PhysicalMemory[frame + i, j] = " ";
                    }
                }
            }

            for (int i = 0; i < 4; i++)
            {
                UserStorage.PhysicalMemory[frame + offset, i] = dataLine[i].ToString();
            }

            Trace.WriteToTrace("Virtual address: " + location + ", Physical address: " + (frame + offset));
        }

        //My TLB was entries 0 - 5. On SR60 instruction, its not in the TLB. So I'm guessing I just add an entry, give R
        public static void WriteToMemoryAndAllocateBlock(int location, String dataLine, int PTR, ProcessControlBlock p)
        {
            int page = location / 10;
            int offset = location % 10;

            string temp = UserStorage.PhysicalMemory[PTR + page, 0];

            if (temp.Equals("&"))
            {
                Trace.WriteToTrace("Virtual address: " + location);
                throw new PageFaultException();
            }

            int frame = int.Parse(temp);

            while (dataLine.Length < 4)
            {
                dataLine += " ";
            }

            if (UserStorage.PhysicalMemory[frame, 0] == null)
            {
                for (int i = 0; i < 10; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        UserStorage.PhysicalMemory[frame + i, j] = " ";
                    }
                }
            }

            for (int i = 0; i < 4; i++)
            {
                UserStorage.PhysicalMemory[frame + offset, i] = dataLine[i].ToString();
            }

            Trace.WriteToTrace("Virtual address: " + location + ", Physical address: " + (frame + offset));
        }

        public static void WriteFrameToMemory(Frame f, int loc)
        {
            String str = "";
            for (int i = 0; i < f.Data.Length; i++)
            {
                str += f.Data[i];
            }

            while (str.Length < 40)
            {
                str += " ";
            }

            int count = 0;

            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    physicalMemory[loc + i, j] = str[count].ToString();
                    count++;
                }
            }
        }

        public static int VirtualToPhysical(int virtualAddress, int PTR)
        {
            int page = virtualAddress / 10;
            int offset = virtualAddress % 10;

            string temp = UserStorage.PhysicalMemory[PTR + page, 0];

            if (temp.Equals("&"))
            {
                //not in the page table yet
                throw new PageFaultException();
            }

            int frame = int.Parse(temp);

            int realAddress = frame + offset;

            Trace.WriteToTrace("Virtual address: " + virtualAddress + ",  real address: " + realAddress);

            return realAddress;
        }

        public static Frame GetFrameForRealAddress(int realAddress)
        {
            String line = "";

            int addr = (realAddress / 10) * 10;

            for (int i = addr; i < addr + 10; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    line += physicalMemory[i, j];
                }
            }

            while (line.Length < 40)
            {
                line += " ";
            }

            char[] tmp = new char[40];

            for (int i = 0; i < tmp.Length; i++)
            {
                tmp[i] = line[i];
            }

            Frame f = new Frame(addr / 10, tmp);
            return f;
        }

        public static void ReleaseFrame(Frame f)
        {
            for (int i = f.ID; i < f.ID + 10; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    UserStorage.physicalMemory[i, j] = null;
                }
            }
        }
    }
}
